package com.lmk.ms.common.db.influx.service;

import com.influxdb.client.InfluxDBClient;
import com.influxdb.client.QueryApi;
import com.influxdb.client.WriteApi;
import com.influxdb.client.domain.WritePrecision;
import com.influxdb.query.dsl.Flux;
import com.lmk.ms.common.db.influx.config.InfluxProperties;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nonnull;
import java.time.temporal.ChronoUnit;
import java.util.List;

/**
 * InfluxDB 服务类 <br />
 * 需在应用中手动导入
 *
 * @author laomake@hotmail.com
 * @version 1.0
 * @date 2022/9/21
 */
@Slf4j
public class InfluxService {

    InfluxProperties influxProperties;

    InfluxDBClient influxDBClient;

    private WriteApi writeApi;

    private QueryApi queryApi;

    public InfluxService(InfluxProperties influxProperties, InfluxDBClient influxDBClient) {
        this.influxProperties = influxProperties;
        this.influxDBClient = influxDBClient;
    }

    @PreDestroy
    public void destroy() {
        if (writeApi != null) {
            try {
                writeApi.close();
            } catch (Exception e) {
                log.error("writeApi 销毁失败：", e);
            }
        }
    }

    public InfluxProperties getInfluxProperties() {
        return influxProperties;
    }

    public InfluxDBClient getInfluxDBClient() {
        return influxDBClient;
    }

    protected WriteApi getWriteApi() {
        if (writeApi == null) {
            synchronized (this) {
                if (writeApi == null) {
                    writeApi = influxDBClient.makeWriteApi();
                }
            }
        }
        return writeApi;
    }

    protected QueryApi getQueryApi() {
        if (queryApi == null) {
            synchronized (this) {
                if (queryApi == null) {
                    queryApi = influxDBClient.getQueryApi();
                }
            }
        }
        return queryApi;
    }

    /**
     * 写入数据，默认时间戳单位为纳秒（ns）
     *
     * @param data
     * @param <T>
     * @return
     */
    public <T> boolean writeData(T data) {
        return writeData(WritePrecision.NS, data);
    }

    /**
     * 写入数据，指定时间戳单位
     *
     * @param writePrecision
     * @param data
     * @param <T>
     * @return
     */
    public <T> boolean writeData(WritePrecision writePrecision, T data) {
        boolean result = true;
        try {
            getWriteApi().writeMeasurement(writePrecision, data);
        } catch (Exception e) {
            log.error("InfluxDB写入数据错误：", e);
            result = false;
        }
        return true;
    }

    /**
     * 写入数据列表，默认时间戳单位为纳秒（ns）
     *
     * @param dataList
     * @param <T>
     * @return
     */
    public <T> boolean writeDataList(List<T> dataList) {
        return writeDataList(WritePrecision.NS, dataList);
    }

    /**
     * 写入数据列表，指定时间戳单位
     *
     * @param writePrecision
     * @param dataList
     * @param <T>
     * @return
     */
    public <T> boolean writeDataList(WritePrecision writePrecision, List<T> dataList) {
        boolean result = true;
        try {
            getWriteApi().writeMeasurements(writePrecision, dataList);
        } catch (Exception e) {
            log.error("InfluxDB写入数据错误：", e);
            result = false;
        }
        return true;
    }

    /**
     * 查询对象列表
     *
     * @param flux
     * @param type
     * @param <T>
     * @return
     */
    public <T> List<T> queryList(Flux flux, Class<T> type) {
        return queryList(flux.toString(), type);
    }

    /**
     * 查询对象列表
     *
     * @param query
     * @param type
     * @param <T>
     * @return
     */
    public <T> List<T> queryList(String query, Class<T> type) {
        return getQueryApi().query(query, type);
    }

    /**
     * 查询时间范围内的数据，时间值应为负数
     *
     * @param start
     * @param stop
     * @param unit
     * @param type
     * @param <T>
     * @return
     */
    public <T> List<T> queryByTimeRange(@Nonnull final Long start, @Nonnull final Long stop, @Nonnull final ChronoUnit unit, Class<T> type) {
        Flux flux = Flux.from(influxProperties.getBucket()).range(start, stop, unit);
        return queryList(flux.toString(), type);
    }

    /**
     * 查询过去一段时间内的数据，时间值应为负数
     *
     * @param start
     * @param unit
     * @param type
     * @param <T>
     * @return
     */
    public <T> List<T> queryByTimeStart(@Nonnull final Long start, @Nonnull final ChronoUnit unit, Class<T> type) {
        Flux flux = Flux.from(influxProperties.getBucket()).range(start, unit);
        return queryList(flux.toString(), type);
    }
}
